﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Drawing;
using System.Drawing.Imaging;
using System.Runtime.InteropServices;
using DIPLib.Filters;

namespace DIPLib
{
    public class DipOperations
    {
        private Bitmap mOriginalBitmap;
        private Bitmap mProcessingBitmap;
        private BitmapData bmpData;

        // Byte array contain rgb data to processing
        private byte[] mRgbData;

        public DipOperations(Bitmap bmp)
        {
            mOriginalBitmap = (Bitmap)bmp.Clone();
        }

        public static Bitmap ConvertToGrayImage(Bitmap original)
        {
            Bitmap newBitmap = new Bitmap(original.Width, original.Height);

            Graphics g = Graphics.FromImage(newBitmap);

            ColorMatrix colorMatrix = new ColorMatrix(
               new float[][]
                  {
                     new float[] {.3f, .3f, .3f, 0, 0},
                     new float[] {.59f, .59f, .59f, 0, 0},
                     new float[] {.11f, .11f, .11f, 0, 0},
                     new float[] {0, 0, 0, 1, 0},
                     new float[] {0, 0, 0, 0, 1}
                  });

            ImageAttributes attributes = new ImageAttributes();

            attributes.SetColorMatrix(colorMatrix);

            g.DrawImage(original, new Rectangle(0, 0, original.Width, original.Height),
               0, 0, original.Width, original.Height, GraphicsUnit.Pixel, attributes);

            g.Dispose();
            return newBitmap;
        }

        private void lockBit()
        {
            mProcessingBitmap = (Bitmap)mOriginalBitmap.Clone();
            PixelFormat pxf = PixelFormat.Format32bppRgb;

            // Lock the bitmap's bits.
            Rectangle rec = new Rectangle(0, 0, mProcessingBitmap.Width, mProcessingBitmap.Height);
            bmpData = mProcessingBitmap.LockBits(rec, ImageLockMode.ReadWrite, pxf);

            // Get the address of the first line.
            IntPtr ptr = bmpData.Scan0;

            int numBytes = mProcessingBitmap.Width * mProcessingBitmap.Height * 4;
            mRgbData = new byte[numBytes];

            // Copy the RGB values into the array.
            Marshal.Copy(ptr, mRgbData, 0, numBytes);
        }

        private void unlockBit()
        {
            IntPtr ptr = bmpData.Scan0;
            Marshal.Copy(mRgbData, 0, ptr, mRgbData.Length);
            mProcessingBitmap.UnlockBits(bmpData);
        }

        #region Histogram processing

        public Bitmap histogram()
        {
            lockBit();
            HistogramProcessing.applyHistEqualization(mRgbData, mProcessingBitmap.Width, mProcessingBitmap.Height);
            unlockBit();
            return mProcessingBitmap;
        }

        public int[] getHistogram()
        {
            lockBit();
            unlockBit();
            return HistogramProcessing.getHistogram(mRgbData);
        }

        #endregion

        #region Point Processing
        public Bitmap negative()
        {
            lockBit();
            PointProcessing.negative(mRgbData);
            unlockBit();
            return mProcessingBitmap;
        }

        public Bitmap threshoding(byte threshold)
        {
            lockBit();
            PointProcessing.thresdholding(mRgbData, threshold);
            unlockBit();
            return mProcessingBitmap;
        }

        public Bitmap logarithmic()
        {
            lockBit();
            PointProcessing.logarithmic(mRgbData);
            unlockBit();
            return mProcessingBitmap;
        }

        public Bitmap powerLaw(float power)
        {
            lockBit();
            PointProcessing.powerLaw(mRgbData, power);
            unlockBit();
            return mProcessingBitmap;
        }
        #endregion

        #region Neighbourhood Operations
        public Bitmap minFiltering(byte matrixSize, byte edgeApproach)
        {
            lockBit();
            MinFilter.doIt(mRgbData, mOriginalBitmap.Width, mOriginalBitmap.Height, matrixSize, edgeApproach);
            unlockBit();
            return mProcessingBitmap;
        }

        public Bitmap maxFiltering(byte matrixSize, byte edgeApproach)
        {
            //if ((matrixSize < 3) || (matrixSize > 35) || (matrixSize % 2 == 0))
            //    throw new ArgumentException();
            lockBit();
            MaxFilter.doIt(mRgbData, mOriginalBitmap.Width, mOriginalBitmap.Height, matrixSize, edgeApproach);
            unlockBit();
            return mProcessingBitmap;
        }

        public Bitmap medianFiltering(byte matrixSize, byte edgeApproach)
        {
            //if ((matrixSize < 3) || (matrixSize > 35) || (matrixSize % 2 == 0))
            //    throw new ArgumentException();
            lockBit();
            MedianFilter.doIt(mRgbData, mOriginalBitmap.Width, mOriginalBitmap.Height, matrixSize, edgeApproach);
            unlockBit();
            return mProcessingBitmap;
        }

        public Bitmap smoothingFiltering(float[] magMatrix, byte edgeApproach)
        {
            lockBit();
            SmoothingFilter.doIt(mRgbData, mOriginalBitmap.Width, mOriginalBitmap.Height, magMatrix, edgeApproach);
            unlockBit();
            return mProcessingBitmap;
        }
        #endregion

        #region Morphology
        public Bitmap erosion(byte[] magMatrix)
        {
            lockBit();
            Erosion.doIt(mRgbData, mOriginalBitmap.Width, mOriginalBitmap.Height, magMatrix);
            unlockBit();
            return mProcessingBitmap;
        }

        public Bitmap dilation(byte[] magMatrix)
        {
            lockBit();
            Dilation.doIt(mRgbData, mOriginalBitmap.Width, mOriginalBitmap.Height, magMatrix);
            unlockBit();
            return mProcessingBitmap;
        }

        public Bitmap opening(byte[] magMatrix)
        {
            lockBit();
            CompoundOperations.opening(mRgbData, mOriginalBitmap.Width, mOriginalBitmap.Height, magMatrix);
            unlockBit();
            return mProcessingBitmap;
        }

        public Bitmap closing(byte[] magMatrix)
        {
            lockBit();
            CompoundOperations.closing(mRgbData, mOriginalBitmap.Width, mOriginalBitmap.Height, magMatrix);
            unlockBit();
            return mProcessingBitmap;
        }

        public Bitmap boundaryExtraction()
        {
            lockBit();
            BoundaryExtraction.doIt(mRgbData, mOriginalBitmap.Width, mOriginalBitmap.Height);
            unlockBit();
            return mProcessingBitmap;
        }
        #endregion

        #region Spatial Filtering 2: Laplacian & Sobel

        public Bitmap laplacian(int[] magMatrix)
        {
            lockBit();
            Laplacian.doIt(mRgbData, mOriginalBitmap.Width, mOriginalBitmap.Height, magMatrix);
            unlockBit();
            return mProcessingBitmap;
        }
        public Bitmap sobel()
        {
            lockBit();
            Sobel.doIt(mRgbData, mOriginalBitmap.Width, mOriginalBitmap.Height);
            unlockBit();
            return mProcessingBitmap;
        }

        #endregion

        #region Zoom
        public Bitmap zoomInZeroOrder(int level)
        {
            lockBit();
            byte[] matrix = ZoomIn.zeroOrder(mRgbData, mOriginalBitmap.Width, mOriginalBitmap.Height, 3);

            Bitmap ret = new Bitmap(mOriginalBitmap.Width * level, mOriginalBitmap.Height * level);
            BitmapData dstData = ret.LockBits(
                new Rectangle(0, 0, ret.Width, ret.Height),
                ImageLockMode.ReadWrite, PixelFormat.Format32bppArgb);
            IntPtr ptr = dstData.Scan0;
            Marshal.Copy(matrix, 0, ptr, matrix.Length);
            ret.UnlockBits(dstData);
            return ret;
        }
        #endregion
    }
}
